// Copyright 2017 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT

#ifndef ZIRCON_KERNEL_HYPERVISOR_INCLUDE_HYPERVISOR_TRAP_MAP_H_
#define ZIRCON_KERNEL_HYPERVISOR_INCLUDE_HYPERVISOR_TRAP_MAP_H_

#include <fbl/arena.h>
#include <fbl/intrusive_wavl_tree.h>
#include <fbl/ref_ptr.h>
#include <hypervisor/state_invalidator.h>
#include <kernel/semaphore.h>
#include <object/port_dispatcher.h>

namespace hypervisor {

// Blocks on allocation if the arena is empty.
class BlockingPortAllocator final : public PortAllocator {
 public:
  BlockingPortAllocator();

  zx_status_t Init() TA_NO_THREAD_SAFETY_ANALYSIS;
  PortPacket* AllocBlocking();
  virtual void Free(PortPacket* port_packet) override;

 private:
  Semaphore semaphore_;
  fbl::TypedArena<PortPacket, fbl::Mutex> arena_;

  PortPacket* Alloc() override;
};

// Describes a single trap within a guest.
class Trap : public fbl::WAVLTreeContainable<ktl::unique_ptr<Trap>> {
 public:
  Trap(uint32_t kind, zx_gpaddr_t addr, size_t len, fbl::RefPtr<PortDispatcher> port, uint64_t key);
  ~Trap();

  zx_status_t Init();
  zx_status_t Queue(const zx_port_packet_t& packet, StateInvalidator* invalidator);

  zx_gpaddr_t GetKey() const { return addr_; }
  bool Contains(zx_gpaddr_t val) const { return val >= addr_ && val < addr_ + len_; }
  bool HasPort() const { return !!port_; }

  uint32_t kind() const { return kind_; }
  zx_gpaddr_t addr() const { return addr_; }
  size_t len() const { return len_; }
  uint64_t key() const { return key_; }

 private:
  const uint32_t kind_;
  const zx_gpaddr_t addr_;
  const size_t len_;
  const fbl::RefPtr<PortDispatcher> port_;
  const uint64_t key_;  // Key for packets in this port range.
  BlockingPortAllocator port_allocator_;
};

// Contains all the traps within a guest.
class TrapMap {
 public:
  zx_status_t InsertTrap(uint32_t kind, zx_gpaddr_t addr, size_t len,
                         fbl::RefPtr<PortDispatcher> port, uint64_t key);
  zx_status_t FindTrap(uint32_t kind, zx_gpaddr_t addr, Trap** trap);

 private:
  using TrapTree = fbl::WAVLTree<zx_gpaddr_t, ktl::unique_ptr<Trap>>;

  DECLARE_SPINLOCK(TrapMap) lock_;
  TrapTree mem_traps_ TA_GUARDED(lock_);
#ifdef ARCH_X86
  TrapTree io_traps_ TA_GUARDED(lock_);
#endif  // ARCH_X86

  TrapTree* TreeOf(uint32_t kind);
};

}  // namespace hypervisor

#endif  // ZIRCON_KERNEL_HYPERVISOR_INCLUDE_HYPERVISOR_TRAP_MAP_H_
